/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, Hardware
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "engine_service.h"
#include <mutex>
#include <utility>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/time.h>

#include "platform/common/rs_log.h"

using namespace OHOS::Rosen;

namespace OHOS {
namespace Render3D {
EngineService::Message::Message(const std::function<EngineTask>& task)
    : task_(std::move(task))
{}

EngineService::Message::Message(EngineService::Message&& msg)
    : task_(std::move(msg.task_)), pms_(std::move(msg.pms_)), ftr_(std::move(msg.ftr_))
{}

EngineService::Message& EngineService::Message::operator=(EngineService::Message&& msg)
{
    task_ = std::move(msg.task_);
    pms_ = std::move(msg.pms_);
    ftr_ = std::move(msg.ftr_);
    return *this;
}

void EngineService::Message::Execute()
{
    task_();
    Finish();
}

void EngineService::Message::Finish()
{
    pms_.set_value();
}

std::shared_future<void> EngineService::Message::GetFuture()
{
    return std::move(ftr_);
}

EngineService& EngineService::GetInstance()
{
    static EngineService service;
    return service;
}

void EngineService::Register(int32_t key, EGLContext context)
{
    RS_LOGI("EngineService::Register start --- key: %d, context: %p", (int)key, (void*)context);
    (void)key;
    if (eglContext_ != EGL_NO_CONTEXT || eglContext_ != context) {
        eglContext_ = context;
    }

    if (registerViewCount_++ == 0) {
        Start();
    }
}

void EngineService::Unregister(int32_t key)
{
    RS_LOGI("EngineService::Unregister start --- key: %d", (int)key);
    (void)key;

    if (--registerViewCount_ == 0) {
        Stop();
    }
}

void EngineService::PushSyncMessage(const std::function<EngineTask>& task)
{
    RS_LOGI("EngineService::PushSyncMessage start");
    std::shared_future<void> ftr;
    {
        std::lock_guard<std::mutex> lk(messageQueueMut_);
        ftr = messageQueue_.emplace(std::move(task)).GetFuture();
        messageQueueCnd_.notify_one();
    }
    RS_LOGI("EngineService::PushSyncMessage wait");
    if (ftr.valid()) {
        ftr.get();
    }
    
    RS_LOGI("EngineService::PushSyncMessage end");
}

std::shared_future<void> EngineService::PushAsyncMessage(const std::function<EngineTask>& task)
{
    std::lock_guard<std::mutex> lk(messageQueueMut_);
    Message& msg = messageQueue_.emplace(std::move(task));
    messageQueueCnd_.notify_one();
    return msg.GetFuture();
}

EngineService::~EngineService()
{
    {
        std::lock_guard<std::mutex> lk(messageQueueMut_);
        while (!messageQueue_.empty()) {
            messageQueue_.front().Finish();
            messageQueue_.pop();
        }
    }
    
    if (loop_.joinable()) {
        loop_.join();
    }
}

void EngineService::Start()
{
    RS_LOGI("EngineService::Start start");
    if (loop_.joinable()) {
        loop_.join();
    }

    loop_ = std::thread(std::bind(&EngineService::EngineThread, this));
    PushAsyncMessage(std::bind(&EngineService::SetName, this));
    PushAsyncMessage(std::bind(&EngineService::InitEngine, this));
    RS_LOGI("EngineService::Start end");
}

void EngineService::Stop()
{
    PushAsyncMessage(std::bind(&EngineService::DeInitEngine, this));
}

void EngineService::InitEngine()
{
    RS_LOGI("EngineService::InitEngine start");
    if (eglContext_ == EGL_NO_CONTEXT) {
        RS_LOGI("EngineService::InitEngine end with no context");
        return;
    }
    RS_LOGI("EngineService::InitEngine end");
}

void EngineService::DeInitEngine()
{
    RS_LOGI("EngineService::DeInitEngine start");
    exit_ = true;
}

void EngineService::EngineThread()
{
    exit_ = false;
    RS_LOGI("EngineService::EngineThread loop start");
    do {
        RS_LOGI("EngineService::EngineThread wait for one message");
        std::unique_lock<std::mutex> lk(messageQueueMut_);
        messageQueueCnd_.wait(lk, [this] { return !messageQueue_.empty(); });
        Message msg(std::move(messageQueue_.front()));
        messageQueue_.pop();
        lk.unlock();
        msg.Execute();
        RS_LOGI("EngineService::EngineThread execute one message");
    } while (!exit_);
    RS_LOGI("EngineService::EngineThread execute exit");
}

void EngineService::SetName()
{
    RS_LOGI("EngineService::SetName start");
    prctl(PR_SET_NAME, "Engine Service Lume", 0, 0, 0);
}
} // namespace OHOS
} // namespace Render3D